function SpTreeView() {
  var _hashParameter = Library.WebControl.Hashtable.CreateHashtable();
  var _expenedClass = "tvexpend";
  var _collapseClass = "tvcollapse";
  var _effectTime;
  var _effectStep;
  var _effectDisplay;
  var _instance = this;
  var _util = Library.WebControl.Util;
  var _id;
  var _expendedCallbackType = "3";
  var _customCallbackType = "1";
  var _dataCallback = "2";
  var _clientName;
  var _callbackInExpend;
  var _beforeExpended = null;
  var _beforeCallback = null;
  this.Init = function(clientName, id, effectDisplay, effectTime, effectStep, callback) {
    _clientName = clientName;
    _globalHashObject.Add(clientName, _instance);
    window[clientName] = _instance;
    _id = id;
    _effectDisplay = effectDisplay;
    _effectStep = effectTime;
    _effectTime = effectTime;
    _callbackInExpend = callback;
  };
  function getLiFromSpan(span) {
    return _util.GetObjectById(span).FindParent("div", "tvitem");  //span.parentNode.parentNode;
  };
  function commonCallback(parameter, callbackType, objectCallback, functionCallback) {
    if (_beforeCallback != null)
      _beforeCallback(_instance, {});
    if (!functionCallback || typeof (functionCallback) != "function") {
      functionCallback = function(s, e) {
      };
    }
    var ajaxRequest = Library.WebControl.Ajax.CreateRequest();
    ajaxRequest.SetData("__SPTREEVIEWF_=" + callbackType + '&__SPTREEVIEWSID_=' + _id + '&__SPTREEVIEWRQP_=' + encodeURIComponent(parameter) + '&' + _hashParameter.ToQuery());
    _hashParameter.Clear();
    ajaxRequest.SendCallback({
      T: callbackType,
      P: parameter,
      F: functionCallback,
      O: objectCallback
    }, callbackComplete);
  };
  function callbackComplete(s, e) {
    var argsResult = _util.ParseAjaxResponse(e.ResponseText);
    argsResult.Parameter = s.P;
    argsResult.CallbackType = s.T;
    if (!argsResult.IsValid) {
      alert(argsResult.Result);
    }
    else {
      if (s.T == _expendedCallbackType) {
        var li = s.O.Li;
        var node = li.firstChild;
        while (node != null) {
          var next = node.nextSibling;
          //if (node.nodeName.toLocaleLowerCase() != "div") {
          //	li.removeChild(node);
          //}
          if (_util.GetClass(node) != "tvtitle")
            li.removeChild(node);
          node = next;
        }


        s.O.Sp.setAttribute("onclick", _clientName + ".Collapse(this);");
        _util.SetClass(s.O.Sp, _expenedClass);
        var divDummy = document.createElement("div");
        _util.EvalScriptAndHtml(argsResult.Data, divDummy);
        while (divDummy.firstChild) {
          s.O.Li.appendChild(divDummy.firstChild);
        }
      }
      else
        if (s.T == _customCallbackType) {
        _util.EvalScriptAndHtml(argsResult.Data, _id);

      }
    }
    s.F(_instance, argsResult);
  };
  this.Collapse = function(sp) {
    var li = getLiFromSpan(sp);
    var divTreeView = li.FindLastChild("div", "sptreeview");
    if (divTreeView != null) {
      Library.WebControl.Effect.Hide({
        Object: divTreeView,
        Type: _effectDisplay,
        Time: _effectTime,
        Step: _effectStep
      });
    }

    sp.setAttribute("onclick", _clientName + ".Expend(this);");
    sp.onclick = function() {
      _instance.Expend(sp);
    };

    _util.SetClass(sp, _collapseClass);

  };
  function parseChildNode(ul, f) {
    var nodes = new Array();
    var ul = _util.GetObjectById(ul);
    ul.Foreach(function(s, e) {
      if (!f(s)) {
        e.Break = true;
      } else {
        nodes.push(s);
      }
    });
    return nodes;
  };
  this.IsExpended = function(n) {

    var d = _util.FindFirstChild(n, "div");
    return _util.FindFirstChild(d, "div", _expenedClass) != null;

  };

  this.RefreshSubTree = function(key) {
    if (key == null) return;
    var li = null;
    if (typeof (key) == "string") li = this.FindNodeByKey(key);
    else {
      li = key;
    }
    if (li != null) {
      var node = _FO(li);
      var data = node.FindLastChild("div", "sptreeview");
      if (data != null) {
        node.removeChild(data);
        setTimeout(function() { _instance.Expend(node.FindFirstChild("div").FindFirstChild("div")); }, 20);
      }
    }
  };
  this.FindNodeByKey = function(k) {
    if (typeof (k) != "string") return null;
    var obj = _util.GetObjectById(_id);
    var node = null;
    var childs = parseChildNode(obj, function(n) {
      if (_instance.GetKeyFromNode(n) == k) {
        node = n;
        return false;
      }
      return true;
    });
    if (node != null) return node;
    return findNode(childs, k);
  };
  this.FindNodesByLevel = function(l) {
    if (l == null) return new Array();
    l = parseInt(l.toString());
    if (isNaN(l)) return new Array();
    var obj = _util.GetObjectById(_id);
    var childs = parseChildNode(obj, function(n) { return true; });
    return findNsByLv(l, childs);
  };
  this.FindParentNode = function(node) {
    node = _util.GetObjectById(node);
    if (node.GetClass() == "tvitem") {
      return node.FindParent("div", "tvitem");
    }
    return null;
  };
  function findNsByLv(l, ns) {
    var arrs = new Array();
    if (ns.length == 0) arrs;
    var lv = _instance.GetLevelFromNode(ns[0]);
    lv = parseInt(lv);
    if (lv == l) return ns;

    for (var i = 0; i < ns.length; i++) {
      var n = ns[i];
      var sub = findSubTree(n);
      if (sub != null) {
        var childs = parseChildNode(sub, function(p) { return true; });
        var cl = findNsByLv(l, childs);
        for (var j = 0; j < cl.length; j++) {
          arrs.push(cl[j]);
        }
      }
    }
    return arrs;
  };
  function findNode(lis, k) {
    for (var i = 0; i < lis.length; i++) {
      var li = lis[i];
      var ul = findSubTree(li);
      if (ul != null) {
        var node = null;
        var childLis = parseChildNode(ul, function(n) {
          if (_instance.GetKeyFromNode(n) == k) {
            node = n;
            return false;
          }
          return true;
        });
        if (node != null) return node;
        node = findNode(childLis, k);
        if (node != null) return node;
      }
    }

    return null;
  };
  this.GetKeyFromNode = function(n) {
    if (n != null) {
      if (n.getAttribute) {
        return n.getAttribute("sptnk");
      }
    }
    return "";
  };
  this.GetLevelFromNode = function(n) {
    if (n != null) {
      if (n.getAttribute) {
        return n.getAttribute("sptnl");
      }
    }
    return "";
  };
  function findSubTree(li) {
    return _util.FindLastChild(li, "div", "sptreeview");
  };
  this.SendCallback = function(parameter, callbackFunction) {
    commonCallback(parameter, _customCallbackType, {}, callbackFunction);
  };
  this.SendDataCallback = function(parameter, callbackFunction) {
    commonCallback(parameter, _dataCallback, {}, callbackFunction);
  };
  this.Expend = function(sp) {

    var li = getLiFromSpan(sp);
    if (li.childNodes.length >= 2) {
      if (!_callbackInExpend) {

        //				Library.WebControl.Effect.Show({
        //					Object: li.childNodes[1],
        //					Type: _effectDisplay,
        //					Time: _effectTime,
        //					Step: _effectStep
        //				});     
        var divTreeView = li.FindLastChild("div", "sptreeview");
        if (divTreeView != null) {
          Library.WebControl.Effect.Show({
            Object: divTreeView,
            Type: _effectDisplay,
            Time: _effectTime,
            Step: _effectStep
          });
        }


        sp.setAttribute("onclick", _clientName + ".Collapse(this);");
        sp.onclick = function() {
          _instance.Collapse(sp);
        };
        _util.SetClass(sp, _expenedClass);
        return;
      } else {
      }
    }
    var k = li.getAttribute("sptnk");
    var lv = li.getAttribute("sptnl");

    if (_beforeExpended != null) {
      var beforeExpendedEventArgs = {
        Cancel: false,
        Key: k,
        Level: lv,
        Node: li
      };
      _beforeExpended(_instance, beforeExpendedEventArgs);

      if (beforeExpendedEventArgs.Cancel) {
        return;
      }
    }
    _hashParameter.Add("__SPTREEVIEWPK_", k);
    _hashParameter.Add("__SPTREEVIEWPL_", lv);
    commonCallback("", _expendedCallbackType, {
      Li: li,
      Sp: sp
    }, function(s, e) {
    });

  };


  this.AddParameter = function(k, v) {
    _hashParameter.Add(k, v);
  };


  this.AddHandler = function(t, f) {
    switch (t) {
      case 1:
        _beforeExpended = f;
        break;
      case 2:
        _beforeCallback = f;
        break;
    }
  };
};

